/* eslint-disable no-nested-ternary */
/* eslint-disable no-restricted-syntax */
/* eslint-disable guard-for-in */
/**
 * num 小于0，左缩进num*2个空格； 大于0，右缩进num*2个空格。
 * @param {string} str 代码
 * @param {number} num 缩进次数
 * @param {number} len 【可选】缩进单位，空格数
 */
export function indent(str, num, len = 2) {
  if (num === 0) return str
  const isLeft = num < 0; const result = []; let reg; let
    spaces = ''
  if (isLeft) {
    num *= -1
    reg = new RegExp(`(^\\s{0,${num * len}})`, 'g')
  } else {
    for (let i = 0; i < num * len; i++) spaces += ' '
  }

  str.split('\n').forEach(line => {
    line = isLeft ? line.replace(reg, '') : spaces + line
    result.push(line)
  })
  return result.join('\n')
}

// 首字母大小
export function titleCase(str) {
  return str.replace(/( |^)[a-z]/g, L => L.toUpperCase())
}

// 下划转驼峰
export function camelCase(str) {
  return str.replace(/-[a-z]/g, str1 => str1.substr(-1).toUpperCase())
}

export function isNumberStr(str) {
  return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
}

export const exportDefault = 'export default '

export const beautifierConf = {
  html: {
    indent_size: '2',
    indent_char: ' ',
    max_preserve_newlines: '-1',
    preserve_newlines: false,
    keep_array_indentation: false,
    break_chained_methods: false,
    indent_scripts: 'separate',
    brace_style: 'end-expand',
    space_before_conditional: true,
    unescape_strings: false,
    jslint_happy: false,
    end_with_newline: true,
    wrap_line_length: '110',
    indent_inner_html: true,
    comma_first: false,
    e4x: true,
    indent_empty_lines: true
  },
  js: {
    indent_size: '2',
    indent_char: ' ',
    max_preserve_newlines: '-1',
    preserve_newlines: false,
    keep_array_indentation: false,
    break_chained_methods: false,
    indent_scripts: 'normal',
    brace_style: 'end-expand',
    space_before_conditional: true,
    unescape_strings: false,
    jslint_happy: true,
    end_with_newline: true,
    wrap_line_length: '110',
    indent_inner_html: true,
    comma_first: false,
    e4x: true,
    indent_empty_lines: true
  }
}

function stringify(obj) {
  return JSON.stringify(obj, (key, val) => {
    if (typeof val === 'function') {
      return `${val}`
    }
    return val
  })
}

function parse(str) {
  JSON.parse(str, (k, v) => {
    if (v.indexOf && v.indexOf('function') > -1) {
      return eval(`(${v})`)
    }
    return v
  })
}

export function jsonClone(obj) {
  return parse(stringify(obj))
}

// 深拷贝对象
export function deepClone(obj) {
  const _toString = Object.prototype.toString

  // null, undefined, non-object, function
  if (!obj || typeof obj !== 'object') {
    return obj
  }

  // DOM Node
  if (obj.nodeType && 'cloneNode' in obj) {
    return obj.cloneNode(true)
  }

  // Date
  if (_toString.call(obj) === '[object Date]') {
    return new Date(obj.getTime())
  }

  // RegExp
  if (_toString.call(obj) === '[object RegExp]') {
    const flags = []
    if (obj.global) { flags.push('g') }
    if (obj.multiline) { flags.push('m') }
    if (obj.ignoreCase) { flags.push('i') }

    return new RegExp(obj.source, flags.join(''))
  }

  const result = Array.isArray(obj) ? [] : obj.constructor ? new obj.constructor() : {}

  for (const key in obj) {
    result[key] = deepClone(obj[key])
  }

  return result
}

const toStr = Function.prototype.call.bind(Object.prototype.toString)
export function isObjectObject(t) {
  return toStr(t) === '[object Object]'
}
export function isObjectArray(t) {
  return toStr(t) === '[object Array]'
}
export function isObjectNull(t) {
  return toStr(t) === '[object Null]'
}
export function isObjectUnde(t) {
  return toStr(t) === '[object Undefined]'
}

// 嵌套的方式合并对象
export function assign(target, source) {
  if(source === undefined || source === null) {
    source = {}
  }
  Object.keys(source).forEach(key => {
    if (isObjectObject(source[key])) {
      if (!target[key]) {
        target[key] = {}
      }
      assign(target[key], source[key])
    } else {
      target[key] = source[key]
    }
  })
  return target
}
// 接收一个对象和一个路径，返回路径对应的值或结构
export function pick(obj, paths, callback) {
  if (typeof paths === 'string') {
    let a = paths.split('.')
    let res = obj
    for (let i = 0; i < a.length; i++) {
      res = res[a[i]]
    }
    return res
  }else{
    let res = {}
    if(!paths) {
      console.log('obj', obj, 'paths', paths)
      console.error('paths is not defined')
      return res
    }
    paths.forEach(path => {
      let a = path.split('.')
      let temp = res
      let tempObj = obj
      for (let i = 0; i < a.length; i++) {
        let key = a[i]
        if (i === a.length - 1 && (!callback || callback(path, tempObj[key]))) {
          temp[key] = tempObj[key]
        } else {
          temp[key] = temp[key] || {}
          temp = temp[key]
          tempObj = tempObj[key]
        }
      }
    })
    return res
  }
}

export function isEmpty(t) {
  return isObjectNull(t) || isObjectUnde(t) || t === ''
}
// 比较目标和来源的数据，返回目标中不同的部分
export function pickDiff(target, source, paths){
  let m = pick(target, paths, (path, value)=>{
    let r = pick(source, path)
    if(isObjectUnde(r) || isObjectNull(r)){
      return isEmpty(r) && isEmpty(value) ? false : r !== value
    }else{
      return typeof r === 'object' ? JSON.stringify(r) !== JSON.stringify(value) : r !== value
    }
  })
  delEmptyObj(m)
  return m
}
// 删除对象中的空对象
export function delEmptyObj(o) {
  Object.keys(o).forEach(key => {
    if (isObjectObject(o[key])) {
      if (Object.keys(o[key]).length === 0) {
        delete o[key]
      } else {
        delEmptyObj(o[key])
        if (Object.keys(o[key]).length === 0) {
          delete o[key]
        }
      }
    }
  })
}

// 获取某个节点下的所有子数组容器（兼容 children 为数组或对象的结构）
function getChildArrays(node) {
  const children = node?.__config__?.children
  const arrays = []
  if (Array.isArray(children)) arrays.push(children)
  else if (children && typeof children === 'object') {
    Object.keys(children).forEach(k => {
      const v = children[k]
      if (Array.isArray(v)) arrays.push(v)
    })
  }
  return arrays
}
// 在 drawingList 中查找 target 的祖先中，满足 predicate 的最近一个
export function findAncestorBy(drawingList, target, predicate) {
    let found = null
    const dfs = (node, path) => {
      if (node === target) {
        // 从近到远查找满足条件的祖先
        for (let i = path.length - 1; i >= 0; i--) {
          const anc = path[i]
          if (predicate(anc)) {
            found = anc
            break
          }
        }
        return true
      }
      const containers = getChildArrays(node)
      for (const arr of containers) {
        for (const child of arr) {
          if (dfs(child, path.concat(node))) return true
        }
      }
      return false
    }
    for (const root of drawingList) {
      if (dfs(root, [])) break
    }
    return found
  }
